/*
 * This file is part of the MicroPython project, http://micropython.org/
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2013-2019 Damien P. George
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "py/runtime.h"
#include "py/mperrno.h"
#include "extmod/vfs.h"
#include "extmod/vfs_lfs.h"
#include "lib/littlefs/lfs2.h"
#include "storage.h"
#include "mphalport.h"


#if MICROPY_HW_ENABLE_STORAGE

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 对LittleFS简单移植(创建文件系统、添加文件)

// 定义静态缓存, 不使用malloc(), free()接口(由lfs2_malloc, lfs2_free包装)
int lfs2_read(const struct lfs2_config *c, lfs2_block_t block,lfs2_off_t off, void *buffer, lfs2_size_t size)
{
    int blocknum = FLASH_PART1_START_BLOCK + MICROPY_HW_FS_BLK_OFFS + block;
    return (flash_bdev_readblocks_ext(buffer, blocknum, off, size) == 0) ? (LFS2_ERR_OK) : (LFS2_ERR_IO);
}

int lfs2_prog(const struct lfs2_config *c, lfs2_block_t block, lfs2_off_t off, const void *buffer, lfs2_size_t size)
{
    int blocknum = FLASH_PART1_START_BLOCK + MICROPY_HW_FS_BLK_OFFS + block;
    return (flash_bdev_writeblocks_ext(buffer, blocknum, off, size) == 0) ? (LFS2_ERR_OK) : (LFS2_ERR_IO);
}

int lfs2_erase(const struct lfs2_config *c, lfs2_block_t block)
{
    return LFS2_ERR_OK;  // W806片内FLASH不需要单独erase
}


int lfs2_sync(const struct lfs2_config *c)
{
    return LFS2_ERR_OK;
}

static uint8_t lfs_buffer[4 * 32];  // read_size, prog_size, lookahead_size, file_config_size(cache_size)

lfs2_t lfs2;                 /* 全局数据结构 */
lfs2_file_t file;            /* 文件描述结构 */

struct lfs2_config cfg = {   /* 配置(移植相关)数据结构 */
    /* flash操作相关的接口, 读、写、擦除、同步 */
    .read = lfs2_read,
    .prog = lfs2_prog,
    .erase = lfs2_erase,
    .sync = lfs2_sync,
    /* 最小的定义读、写字节数, 实际读写时的字节数是这里的整数倍 */
    .read_size = 32,
    .prog_size = 32,
    /* 块大小和数量, littlefs规定每个文件至少占用一个块, 块太大则保存不了多少个文件512K/64K=8 */
    .block_size = MICROPY_HW_BLK_SIZE,
    .block_count = MICROPY_HW_BLK_CNT,
    /* 耗损平衡相关, 取值100-1000, 数值越大性能越好但磨损分布不太好, 取值-1则禁止磨损平衡 */
    .block_cycles = 500,
    /* 块缓存大小, lf2允许每个块驻留部分数据到内存, 以降低IO次数, 该值必须是read_size、prog_size的整数倍 */
    /* littlefs2需要read cache, write cache, 每个文件对象(lfs2_file_t)还单独需要一个cache */
    .cache_size = 32,
    /* 先行缓存, 大小必须是8的倍数, 1字节8个bit, 每个bit对应相邻block的空闲情况, 分配块时, 可加快搜索速度 */
    .lookahead_size = 32,
    /* 工具链的C库不支持malloc, free, 定义静态缓存, 把数组首地址写这里, 每个file对象也要一个额外的buffer */
    .read_buffer = lfs_buffer + 0 * 32,
    .prog_buffer = lfs_buffer + 1 * 32,
    .lookahead_buffer = lfs_buffer + 2 * 32,
};

// 挂载文件系统(若不合法则mkfs)
void mount_lfs2(void)
{
    // mount the file system
    int err = lfs2_mount(&lfs2, &cfg);

    // reformat and mount file system if premount failed
    if(err){
        lfs2_format(&lfs2, &cfg);
        lfs2_mount(&lfs2, &cfg);
    }
}

// 取消挂载文件系统
void umount_lfs2(void)
{
    lfs2_unmount(&lfs2);
}

// 根据lfs2_file_open编写, 要给struct lfs2_file_config补充静态缓存(防止malloc分配)
static int open_file_lfs2(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags)
{
    static uint8_t buffer[32];
    static struct lfs2_file_config defaults = {.buffer=buffer/*lfs_buffer+3*32*/, };
    int err = lfs2_file_opencfg(lfs2, file, path, flags, &defaults);
    return err;
}

// 创建文件
static int create_file_lfs2(const char* path, const void* buffer, int len)
{
    //printf("%s:%d open file\n", __func__, __LINE__);
    int ret = open_file_lfs2(&lfs2, &file, path, LFS2_O_CREAT | LFS2_O_RDWR);
    //printf("%s:%d  open result:%d\n", __func__, __LINE__, ret);
    if(ret >= 0){
        ret = lfs2_file_write(&lfs2, &file, buffer, len);
        //printf("%s:%d write result:%d\n", __func__, __LINE__, ret);
        lfs2_file_close(&lfs2, &file);
    }
    return ret;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 构建模板文件(copy from stm32 )

static const char fresh_boot_py[] =
"# boot.py -- run on boot-up\n"
"# can run arbitrary Python, but best to keep it minimal\n"
"\n"
"import uos as os\n"
"import usys as sys\n"
;

static const char fresh_main_py[] =
"# main.py -- put your code here!\n"
;


static const char fresh_readme_txt[] =
"This is a MicroPython board\n"
"\n"
"You can get started right away by writing your Python code in 'main.py'.\n"
"\n"
"For a serial prompt:\n"
" - Windows: you need to go to 'Device manager', right click on the unknown device,\n"
"   Then use a terminal program like Hyperterminal or putty.\n"
" - Mac OS X: use the command: screen /dev/tty.usbmodem*\n"
" - Linux: use the command: screen /dev/ttyACM0\n"
"\n"
"Please visit http://micropython.org/help/ for further help.\n"
;


typedef struct _factory_file_t {
    const char *name;
    size_t len;
    const char *data;
} factory_file_t;

static const factory_file_t factory_files[] = {
    {"boot.py", sizeof(fresh_boot_py) - 1, fresh_boot_py},
    {"main.py", sizeof(fresh_main_py) - 1, fresh_main_py},
    {"README.txt", sizeof(fresh_readme_txt) - 1, fresh_readme_txt},
};


MP_WEAK int factory_reset_make_files(void) {
    for (int i = 0; i < MP_ARRAY_SIZE(factory_files); i++) {
        //printf("%s[%d] make file(%s)\n", __func__, __LINE__, factory_files[i].name);
        if(create_file_lfs2(factory_files[i].name, factory_files[i].data, factory_files[i].len) < 0)
            return -MP_EIO;
    }
    return 0;
}

// 调用uos.VfsLfS2.mkfs()构建文件系统
STATIC mp_obj_t mp_mkfs_call(mp_obj_t vfs, mp_obj_t bdev, qstr meth_name) 
{
    mp_obj_t meth[] = {MP_OBJ_NULL, MP_OBJ_NULL,                                       // [0]-func, [1]-self, 由mp_load_method使用
                       bdev,                                                           // [2]-位置参数
                       MP_OBJ_NEW_QSTR(MP_QSTR_readsize), MP_OBJ_NEW_SMALL_INT(256),   // [3]=key, [4]=val, kw参数
                       MP_OBJ_NEW_QSTR(MP_QSTR_progsize), MP_OBJ_NEW_SMALL_INT(256),   // [5]=key, [6]=val, kw参数
                       MP_OBJ_NEW_QSTR(MP_QSTR_lookahead), MP_OBJ_NEW_SMALL_INT(16),   // [7]=key, [8]=val, kw参数
                      };
    mp_load_method(vfs, meth_name, meth);        // 获取类方法
    return mp_call_method_n_kw(1, 3, meth);      // 调用类方法
}


// 构建文件系统, 并创建默认文件
int factory_reset_create_filesystem(mp_obj_type_t *vfs, mp_obj_t bdev) 
{
    int ret = 0;
    
    // 成功返回None, 失败发出异常
    mp_mkfs_call(vfs, bdev, MP_QSTR_mkfs);
    //printf("%s:%d mp_mkfs_call success.\n", __func__, __LINE__);
    
    mount_lfs2();
    ret = lfs2_mkdir(&lfs2, "/lib");         // 创建/lib子目录
    if(ret == LFS2_ERR_OK){       
        ret = factory_reset_make_files();    // 创建各个子文件
    }
    umount_lfs2();
    
    mp_hal_delay_ms(50);
    return ret; // success
}

#endif // MICROPY_HW_ENABLE_STORAGE

